(function (global, doc) {
    //------------------------------------------------------------处理ujsConfig默认值---------------------------------------------------------    
    var ujsConfig = (function () {
        /**
         * 通用的错误消息（description）处理逻辑.
         * @param {Object} msg
         */
        var _errorHandler = function (msg) {
            var ajaxConfig = this;
            if (!ujs.isWillLogout) {
                if (ajaxConfig.errorCodeDesp === 0) {
                    /**
                     * 规范约定，若有错误，则返回如:{
                     *     code : 1002,
                     *     description : 'error detail'
                     * }
                     */
                    bubble.msg(msg['description']);
                } else {
                    /**
                     * 若错误描述放置到前端js脚本中，则如下调用.
                     */
                    if (ujs.util.ErrorsMap) {
                        bubble.msg(ujs.util.ErrorsMap[msg['code']]);
                    }
                }
                //若错误消息为'session timeout',则1.5s后跳转到登录页.
                if (msg['code'] === ajaxConfig.sessionTimeoutCode) {
                    ujs.isWillLogout = true;
                    window.setTimeout(function () {
                        ujs.util.debug.log({
                            msg: 'toLoginPage:session timeout:' + ajaxConfig.sessionTimeoutCode
                        });
                        ujs.browser.loc.toLoginPage(false);
                    }, 1500);
                    return false;
                }
            }
            return true;
        };

        /**
         * ujs默认配置.
         */
        var _defaultUjsConfig = {
            rootPath: '',  //默认根目录。
            //ujs库及图片目录在项目中的位置
            path: '/ujs/',
            imagePath: '/ujs/images/',
            //空白图片路径.
            emptyImagePath: 'images/common/no-img.png',
            //默认用户头像.        
            defUserIcon: 'images/im/user/user-256.png',
            defGroupIcon: 'images/im/user-group/user-group-256.jpg',
            //日期格式化.            
            dateFormat: 'dd-MN-yyyy',
            dateTimeFormat: 'dd-MN-yyyy hh:mm',
            //页面跳转.
            loginPageUrl: '/login.html',
            indexPageUrl: '/index.html',
            //其它.                
            autoNamePrefix: '_autoName_',
            pageSize: 30,
            //rest api 数据处理.
            ajax: {
                cache: false,
                dataType: 'json',
                crossDomain: true,
                apiPathPrefix: '/rest',
                globalHandler: true,   //是否启用全局数据处理函数.
                sessionTimeoutCode: null,  //session过期时对应的code.
                errorCodeDesp: 0, //错误码描述类型。 0，使用服务器端的错误描述；1，使用客户端的错误描述。
                processUrl: function (options, originalOptions) {
                },
                processData: function (options, originalOptions) {
                    if (options['type']) {
                        var ajaxType = options['type'].toLowerCase();
                        if (ajaxType === 'post' || ajaxType === 'put') {
                            options['contentType'] = 'text/plain';
                        }
                    }
                    if (Object.prototype.toString.call(originalOptions['data']) === '[object Object]') {
                        //serialization json data.
                        options['data'] = JSON.stringify(originalOptions['data']);
                    }
                },
                codeHandler: {},
                handleError: _errorHandler,
                successHandler: function (handler, errorHandler) {
                    var ajaxConfig = this;
                    return function (msg, textStatus, jqXHR) {
                        if (ajaxConfig.globalHandler === true) {
                            msg = JSON.insure(msg);
                            var code = msg['code'];
                            if (code === 1) {
                                return handler && handler(msg, textStatus, jqXHR);
                            } else {
                                var codeHandlerMapping = Object.mapping(ajaxConfig.codeHandler);
                                //TODO: 有问题
                                if (Object.prototype.toString.call(codeHandlerMapping(code)) === '[object Function]') {
                                    codeHandlerMapping(code, msg, textStatus, jqXHR);
                                } else {
                                    if (_errorHandler.call(ajaxConfig, msg) && errorHandler) {
                                        errorHandler(msg, textStatus, jqXHR);
                                    }
                                }
                            }
                        } else {
                            return handler && handler(msg, textStatus, jqXHR);
                        }
                    };
                }
            }
        };
        //应用默认UjsConfig.
        if (typeof window['ujsConfig'] === 'object') {
            var objKeys = 'test,ajax,test';
            for (var key in _defaultUjsConfig) {
                if (typeof window['ujsConfig'][key] === 'undefined') {
                    window['ujsConfig'][key] = _defaultUjsConfig[key];
                }
                if (objKeys.indexOf(',' + key + ',') > -1) {
                    var ujsKeyConfig = window['ujsConfig'][key],
                        defKeyConfig = _defaultUjsConfig[key];
                    for (var subKey in defKeyConfig) {
                        if (typeof ujsKeyConfig[subKey] === 'undefined') {
                            ujsKeyConfig[subKey] = defKeyConfig[subKey];
                        }
                    }
                }
            }
        } else {
            window['ujsConfig'] = _defaultUjsConfig;
        }

        return window['ujsConfig']
    })();

    //------------------------------------------------------------ujs基础成员------------------------------------------------------------

    var ujs = (function (ujsConfig) {
        /**
         * ujs : ultra javascript.
         * ujs库基础.
         */
        var ujs = window.ujs = {
            /**
             * 一般代码库的版本号是固定的，此处优先取ujsConfig.version的值，是为了能够在appConfig.js文件中指定ujs的版本号.
             */
            version: ujsConfig.version || '1.0.0.30',
            /**
             * 系统系统是否马上要退出，在计划退出与实施退出之间有一段时间间隙。
             * 可以在页面上显示退出提示、执行动画渐隐等逻辑。
             * 在此间隙中收到的错误消息不做处理。
             */
            isWillLogout: false,
            /**
             * ujs配置.
             */
            config: ujsConfig,
            getConfig: function (cfgPath) {
                return JSON.getByPath(this.config, cfgPath);
            },
            /**
             * 获取资源的绝对路径.
             */
            getRes: function (resPath) {
                return this.config['rootPath'] + this.config['path'] + resPath;
            },
            getImgRes: function (resPath, title) {
                return '<img src="{0}" title="{1}", alt="{1}" />'.format(this.getRes(resPath), title || '');
            },
            /**
             * 获取配置的资源的绝对路径.
             */
            getConfigRes: function (resKey) {
                return this.config['rootPath'] + this.config['path'] + this.config[resKey];
            },
            /**
             * 对象扩展.
             */
            extend: function (target, obj) {
                if (typeof obj == 'undefined') {
                    obj = target;
                    target = this;
                }
                if (obj) {
                    for (var key in obj) {
                        if (obj.hasOwnProperty(key)) {
                            target[key] = obj[key];
                        }
                    }
                }
            },
            //为成员起别名.
            alias: function (obj, memberObj, aliasKey) {
                if (aliasKey.indexOf(',') > -1) {
                    var keys = aliasKey.split(',');
                    for (var i = 0; i < keys.length; i++) {
                        obj[keys[i]] = memberObj;
                    }
                } else {
                    obj[aliasKey] = memberObj;
                }
            },
            /**
             * 创建命名空间.
             * @method ns
             * @param namespaceStr {String} eg:'ujs.data'
             * @return {Object} 命名空间对象.
             */
            ns: function (namespaceStr) {
                var namespaceList = namespaceStr.split('.'),
                    itemTemp = window,
                    i,
                    len;
                for (i = 0, len = namespaceList.length; i < len; i++) {
                    itemTemp = itemTemp[namespaceList[i]] = itemTemp[namespaceList[i]] || {};
                }
                return itemTemp;
            },
            /**
             * all response common handler.
             * 返回格式默认为JSON，如:
             * {
             *     code : 1,
             *     tags : ['tagid1', 'tagid2']
             * } 或  {
             *     code : 1038,
             *     description : 'session timeout'
             * }
             * 若请求成功，则code === 1, 其它数据与code同级放置。
             * 若请求失败，则code !== 1, 且与code同级存在description键值，以描述错误消息; 若存在多条错误消息，则同级存在errors键值，以描述之。
             */
            handleResponse: function () {
                var ajaxConfig = ujs.config.ajax;
                return ajaxConfig['successHandler'].apply(ajaxConfig, Array.prototype.slice.call(arguments, 0));
            }
        };

        /**
         * 简写ajax通用响应处理函数.
         */
        ujs.alias(ujs, ujs.handleResponse, 'hr');

        return ujs;

    })(window['ujsConfig'] || {});

    //------------------------------------------------------------ujs AMD支持------------------------------------------------------------

    (function (ujs, ujsConfig) {
        ujs.extend({
            /**
             * 定义模块
             */
            define: function (name, ns, factory) {
                var newModule = typeof factory == 'function' ? factory() : factory;
                ns = this.ns(ns);
                if (!ns[name] || typeof newModule != 'object') {
                    return ns[name] = newModule;
                } else {
                    var oldModule = ns[name];
                    for (var key in newModule) {
                        if (newModule.hasOwnProperty(key)) {
                            oldModule[key] = newModule[key];
                        }
                    }
                    return oldModule;
                }
            },
            /**
             * 请求加载模块
             */
            require: function () {
            }
        });
    })(ujs, ujsConfig);

    //------------------------------------------------------------脚本加载模块------------------------------------------------------------

    (function (ujs, ujsConfig, doc, head) {

        ujs.extend({
            loadScript: _loadScript,
            writeScript: _writeScript,
            writeScriptText: _writeScriptText
        });

        /**
         * 写入脚本地址.
         * @param jsPaths {String|Array} 脚本地址.
         * @param jsRelPath {String} 脚本相对地址.
         * @param jsVersion {Number|String} 脚本版本号.
         */
        function _writeScript(jsPaths, jsRelPath, jsVersion) {
            if (jsPaths) {
                jsRelPath = jsRelPath || '';
                jsVersion = jsVersion || Date.now();    //若未指定版本号，则使用时间戳，不允许Client使用缓存。
                if (typeof (jsPaths) === 'string') {
                    jsPaths = [jsPaths];
                }
                for (var i = 0, len = jsPaths.length; i < len; i++) {
                    var jsPath = jsPaths[i];
                    if (jsPath.indexOf('http') != 0) {
                        jsPath = ujs.config['rootPath'] + jsRelPath + jsPaths[i] + "?v=" + jsVersion;
                    }
                    doc.writeln("<script src='" + jsPath + "'></script>");
                }
            }
            return this;
        }

        /**
         * 写入脚本代码.
         * @method this.writeScriptText
         * @param scriptText {String|Array}
         */
        function _writeScriptText(scriptText) {
            if (Object.prototype.toString.call(scriptText) == "[object Array]") {
                scriptText = scriptText.join('\r\n');
            }
            doc.writeln('<script>' + scriptText + '</script>');
        }

        /**
         * 加载JavaScript.
         * @method loadScript
         * @param jsPath {String} 脚本地址.
         * @param cb {Function} 脚本加载完成时回调函数.
         * @param onerror {Function} 脚本加载失败时回调函数.
         * @return this.
         */
        function _loadScript(jsPath, cb, onerror) {
            var script = doc.createElement('script');
            script.onload = function () {
                if (Object.prototype.toString.call(cb) === '[object Function]') {
                    cb();
                }
            };
            script.onerror = function () {
                if (Object.prototype.toString.call(onerror) === '[object Function]') {
                    onerror();
                }
            };
            script.type = 'text/javascript';
            script.src = jsPath;
            head.appendChild(script);
            return this;
        }
    })(ujs, ujsConfig, document, document.head);

    //------------------------------------------------------------样式表加载模块------------------------------------------------------------

    (function (ujs, ujsConfig, doc, head) {

        ujs.extend({
            loadStyle: _loadStyle,
            writeCSS: _writeCSS,
            writeCSSText: _writeCSSText
        });

        /**
         * 写入CSS地址.
         * @param cssPaths {String|Array} CSS地址.
         * @param cssRelPath {String} CSS相对地址.
         * @param cssVersion {Number|String} CSS文件版本号.
         */
        function _writeCSS(cssPaths, cssRelPath, cssVersion) {
            if (cssPaths) {
                cssRelPath = cssRelPath || '';
                cssVersion = cssVersion || Date.now();  //若未指定版本号，则使用时间戳，不允许Client使用缓存。
                if (typeof (cssPaths) === 'string') {
                    cssPaths = [cssPaths];
                }
                for (var i = 0, len = cssPaths.length; i < len; i++) {
                    var cssPath = ujs.config['rootPath'] + cssRelPath + cssPaths[i] + "?v=" + cssVersion,
                        css = doc.createElement('link');
                    css.href = cssPath;
                    this.writeCSSText('@import url(' + cssPath + ');');
                }
            }
        }

        /**
         * 写入CSS代码.
         * @param cssText {String} CSS代码.
         */
        function _writeCSSText(cssText) {
            if (Object.prototype.toString.call(cssText) == "[object Array]") {
                cssText = cssText.join('\r\n');
            }
            doc.writeln('<style>' + cssText + '</style>');
        }

        function _loadStyle(url) {
            if (document.createStyleSheet) {
                document.createStyleSheet(url);
            } else {
                var cssLink = document.createElement('link');
                cssLink.rel = 'stylesheet';
                cssLink.type = 'text/css';
                cssLink.href = url;
                head.appendChild(cssLink)
            }
        }
    })(ujs, ujsConfig, document, document.head);

    //------------------------------------------------------------第三方库加载模块------------------------------------------------------------

    (function (ujs, ujsConfig) {
        /**
         * 外部库路径配置.
         */
        var _libsConfig = {
            'jquery-color': {
                'version': '2.1.2',
                'debug': {
                    'js': [
                        'js/jquery.color.plus-names-2.1.2.js'
                    ]
                }
            },
            'jquery-layout': {
                'version': '1.0.1.1',
                'debug': {
                    'js': [
                        'js/jquery-ui-latest.js', //TODO: spliter resize 依赖于 jquery ui, 去掉这种依赖，不需要.
                        'js/jquery.layout-latest.js'
                    ],
                    'css': [
                        'css/layout-default-latest.css'
                    ]
                }
            },
            'tooltipster': {
                'version': '1.0.1.2',
                'debug': {
                    'js': [
                        'js/jquery.tooltipster.js'
                    ],
                    'css': [
                        'css/tooltipster.css',
                        'css/themes/tooltipster-light.css'
                    ]
                }
            },
            'zip.js': {
                'version': '1.0.1.0',
                'debug': {
                    'js': [
                        'zip.js'
                    ]
                }
            }
        };

        /**
         * 加载第三方代码库.
         * @param name {String} 代码库名字.
         * @param isDebug {Boolean} 是否使用高度模式.
         */
        function _loadLib(name, isDebug) {
            var libCfg = _libsConfig[name],
                meta = libCfg[isDebug ? 'debug' : 'release'];
            //若没有release脚本配置，则使用debug脚本配置.
            meta = meta || libCfg['debug'];
            if (meta) {
                var cssArr = meta['css'],
                    jsArr = meta['js'],
                    relPath = ujs.config['path'] + 'libs/';
                if (cssArr) {
                    for (var i = 0; i < cssArr.length; i++) {
                        this.writeCSSText('@import url(' + ujs.config['rootPath'] + relPath + name + '/' + cssArr[i] + '?v=' + libCfg.version + ');');
                    }
                }
                if (jsArr) {
                    for (var j = 0; j < jsArr.length; j++) {
                        ujs.writeScript(name + '/' + jsArr[j], relPath, libCfg.version);
                    }
                }
            }
        }

        ujs.extend({
            loadLib: _loadLib
        });

    })(ujs, ujsConfig);

    //------------------------------------------------------------语言包加载模块------------------------------------------------------------

    (function (ujs, ujsConfig) {
        ujs.extend({
            loadLan: function (language) {
                if (language) {
                    //语言包是项目特定资源，所有使用域特定文件，添加 window.location.origin。
                    var lanPathPattern = window.location.origin + (language.lanPathPattern || '/res/language/{0}.js');
                    var lanVersion = language.version || this.version;
                    //语言加载器, 默认取URL中参数lan的值；若不存在，则取浏览器的默认语言；若浏览器默认语言包不存在，则取默认语言en-US。
                    var languageMatch = window.location.search.match(new RegExp("[\?\&]lan=([^\&]+)", "i")),
                        lan = ( languageMatch && languageMatch.length > 1 ) ? languageMatch[1] : window.navigator.language;
                    this.writeScript(lanPathPattern.replace('{0}', lan), '', lanVersion);
                    this.writeScriptText('!window.lans && document.writeln("<script src=\'' + lanPathPattern.replace('{0}', 'en-US') + '?v=' + lanVersion + '\'/>\\<\\/script\\\>")');
                }
            }
        });
    })(ujs, ujsConfig);

    //------------------------------------------------------------按配置加载模块------------------------------------------------------------

    (function () {
        ujs.extend({
            load: function (loadConfig) {
                loadConfig = loadConfig || {};
                var ujsPath = this.config['path'];
                //load lan
                this.loadLan(loadConfig['language']);
                //load css            
                if (loadConfig['debug']) {
                    this.writeScript(ujsPath + 'grunt/resource_path/css_res.js', '', this.version);
                    this.writeScriptText([
                        'var allCSSes = ujs.getResPath(' + JSON.stringify(loadConfig) + '),',
                        '    cssesImport = [];',
                        'for(var i=0; i<allCSSes.length; i++){',
                        '    cssesImport.push("@import url(' + this.config['rootPath'] + ujsPath + 'css/" + allCSSes[i] + ");");',
                        '};',
                        'ujs.writeCSSText(cssesImport);'
                    ]);
                } else {
                    this.writeCSS('build/ujs.min.css', ujsPath, this.version);
                }
                //load javascript            
                if (loadConfig['debug']) {
                    this.writeScript(ujsPath + 'grunt/resource_path/js_res.js', '', this.version);
                    this.writeScriptText("ujs.writeScript(ujs.getResPath(" + JSON.stringify(loadConfig) + "), '" + ujsPath + "js/', ujs.version);");
                } else {
                    this.writeScript('build/ujs.min.js', ujsPath, this.version);
                }
                //load libs
                var libs = loadConfig['libs'];
                if (libs) {
                    for (var i = 0; i < libs.length; i++) {
                        this.loadLib(libs[i], loadConfig['debug']);
                    }
                }
                //load apps
                var apps = loadConfig['app'];
                if (apps) {
                    for (var j = 0; j < apps.length; j++) {
                        //每个app的启动脚本不允许缓存，所以不加版本号(writeScript默认使用时间戳作为版本号).
                        this.writeScript(apps[j] + '/load.js', this.config.path + 'app/');
                    }
                }
                //page css 
                var pageCssArr = loadConfig['pageCss'];
                if (pageCssArr) {
                    var pageCssImportCode = [];
                    for (var k = 0; k < pageCssArr.length; k++) {
                        pageCssImportCode.push('@import url(' + pageCssArr[k] + ');');  //页面级CSS路径不作处理.
                    }
                    this.writeCSSText(pageCssImportCode);
                }
                //page js
                var pageJsArr = loadConfig['pageJs'];
                if (pageJsArr) {
                    for (var m = 0; m < pageJsArr.length; m++) {
                        this.writeScript(window.location.origin + pageJsArr[m]); //页面级JS路径不作处理.
                    }
                }
            }
        });
        /**
         * ujs.start更加形象地引用ujs库.
         */
        ujs.alias(ujs, ujs.load, 'start');
    })();
})(window, document);